﻿using Flurl.Http;

using Furion.FriendlyException;

using Microsoft.Extensions.Caching.Memory;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

public class RequestService : ITransient
{
    private readonly IMemoryCache _memoryCache;

    public RequestService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    private const string _cacheSessionId = "MtSessionId";
    private const string _cacheVersion = "MtVersion";
    private const string _productUrl = "https://static.moutai519.com.cn/mt-backend/xhr/front/mall/index/session/get/"; //商品列表
    private const string _shopUrl = "https://static.moutai519.com.cn/mt-backend/xhr/front/mall/resource/get";//门店信息
    private const string _reservationUrl = "https://app.moutai519.com.cn/xhr/front/mall/reservation/add";//预约
    private const string _versionUrl = "https://apps.apple.com/cn/app/i%E8%8C%85%E5%8F%B0/id1600482450";//版本号
    private const string _codeUrl = "https://app.moutai519.com.cn/xhr/front/user/register/vcode";//验证码
    private const string _loginUrl = "https://app.moutai519.com.cn/xhr/front/user/register/login";//登录
    private const string _salt = "2af72f100c356273d46284f6fd1dfc08";
    private const string AesKey = "qbhajinldepmucsonaaaccgypwuvcjaa";
    private const string AesIv = "2018534749963515";
    private const string _MTLat = "30.005667";
    private const string _MTLng = "120.5933";

    #region 拉取商品信息

    /// <summary>
    /// 获取商品信息
    /// </summary>
    /// <returns></returns>
    public async Task<List<ProductEntity>> GetRequestProductList()
    {
        var milliseconds = GetNowMilliseconds();
        var responseStr = await $"{_productUrl}{milliseconds}".GetStringAsync();

        var jObject = JObject.Parse(responseStr);
        if (jObject.GetValue("code").Value<int>() == 2000)
        {
            var dataJObject = jObject["data"];
            _memoryCache.Set(_cacheSessionId, dataJObject["sessionId"].Value<string>());
            var itemList = (JArray)dataJObject["itemList"];
            List<ProductEntity> productList = new List<ProductEntity>();
            foreach (var itemElement in itemList)
            {
                productList.Add(new ProductEntity()
                {
                    itemCode = itemElement["itemCode"].Value<string>(),
                    title = itemElement["title"].Value<string>(),
                    content = itemElement["content"].Value<string>(),
                    picture = itemElement["picture"].Value<string>(),
                    CreatedAt = DateTime.Now
                });
            }
            return productList;
        }
        return null;
    }

    /// <summary>
    /// 当前时间戳
    /// </summary>
    /// <returns></returns>
    private long GetNowMilliseconds()
    {
        var midNight = DateTime.Now.Date;
        var epochStart = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.FromHours(8));
        var timeSpan = midNight.AddHours(-8) - epochStart;
        var milliseconds = (long)timeSpan.TotalMilliseconds;

        return milliseconds;
    }

    #endregion 拉取商品信息

    #region 拉取门店信息

    /// <summary>
    /// 获取门店信息
    /// </summary>
    /// <returns></returns>
    public async Task<List<ShopEntity>> GetRequestShopList()
    {
        var responseStr = await $"{_shopUrl}".GetStringAsync();

        var jObject = JObject.Parse(responseStr);
        if (jObject.GetValue("code").Value<int>() == 2000)
        {
            var shopsUrl = jObject.GetValue("data").Value<JObject>().GetValue("mtshops_pc").Value<JObject>().GetValue("url").Value<string>();

            var shopInnerJson = await shopsUrl.GetStringAsync();
            var shopInnerJObject = JObject.Parse(shopInnerJson);

            List<ShopEntity> shopList = new List<ShopEntity>();
            foreach (var property in shopInnerJObject.Properties())
            {
                var shopId = property.Name;
                var nestedObject = (JObject)property.Value;
                shopList.Add(new ShopEntity()
                {
                    ShopId = shopId,
                    UnbrokenAddress = nestedObject["fullAddress"].Value<string>(),
                    Province = nestedObject["provinceName"].Value<string>(),
                    City = nestedObject["cityName"].Value<string>(),
                    Area = nestedObject["districtName"].Value<string>(),
                    Name = nestedObject["name"].Value<string>(),
                    CompanyName = nestedObject["tenantName"].Value<string>(),
                    Lat = nestedObject["lat"].Value<string>(),
                    Lng = nestedObject["lng"].Value<string>(),
                    CreatedAt = DateTime.Now
                });
            }
            return shopList;
        }
        return null;
    }

    #endregion 拉取门店信息

    #region 预约

    /// <summary>
    /// 获取当前省份下所有店铺中出售指定产品的店铺
    /// </summary>
    /// <param name="province">省份</param>
    /// <param name="itemId">  产品id</param>
    /// <returns></returns>
    public async Task<List<IMTItemInfo>?> GetShopsByProvince(string province, string itemId)
    {
        var milliseconds = GetNowMilliseconds();

        var list = new List<IMTItemInfo>();
        string currentSessionId = await GetCurrentSessionIdAsync();
        var requestUrl =
            $"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/shop/list/slim/v3/{currentSessionId}/{province}/{itemId}/{milliseconds}";
        var response =
            await
                requestUrl
                    .AllowAnyHttpStatus()
                    .GetAsync();
        if (response.StatusCode == 404)
        {
            await RefreshSessionIdAsync();
            await GetShopsByProvince(province, itemId);
        }

        var responseText = await response.GetStringAsync();

        var responseJObject = JObject.Parse(responseText);
        if (responseJObject["code"].Value<int>() != 2000)
        {
            Console.WriteLine($"查询所在省市的投放产品和数量error,{province}-{itemId}");
            return null;
        }

        var data = responseJObject["data"].Value<JObject>();
        var shopList = data["shops"].Value<JArray>();
        foreach (var shop in shopList)
        {
            var shops = shop.Value<JObject>();
            var items = shops["items"].Value<JArray>();
            foreach (var item in items)
            {
                var itemObj = item.Value<JObject>();
                if (itemObj["itemId"].Value<string>() == itemId)
                {
                    list.Add(new IMTItemInfo(
                        shops["shopId"].Value<string>(),
                        itemObj["count"].Value<int>(),
                        itemObj["itemId"].Value<string>(),
                        itemObj["inventory"].Value<int>()
                    ));
                }
            }
        }

        return list;
    }

    /// <summary>
    /// 预约
    /// </summary>
    /// <param name="user">  </param>
    /// <param name="itemId"></param>
    /// <param name="shopId"></param>
    /// <returns></returns>
    public async Task<dynamic> Reservation(UserEntity user, string itemId, string shopId)
    {
        var info = new Dictionary<string, object>
        {
            { "itemId", itemId },
            { "count", 1 }
        };

        var values = new Dictionary<string, object>
        {
            { "itemInfoList", new List<Dictionary<string, object>>() { info } },
            { "sessionId",await GetCurrentSessionIdAsync() },
            {"shopId",shopId},
            {"userId",user.UserId + ""}
        };
        values.Add("actParam", EncryptAES_CBC(JsonConvert.SerializeObject(values).Replace("\\\"", "\"")));
        var requestBody = JsonConvert.SerializeObject(values);

        var response = await _reservationUrl
        .WithHeader("userId", user.UserId + "").WithHeader("Content-Type", "application/json")
        .WithHeader("Connection", "keep-alive").WithHeader("MT-Bundle-ID", "com.moutai.mall")
        .WithHeader("MT-R", "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==")
        .WithHeader("Accept-Encoding", "gzip, deflate, br").WithHeader("Accept-Language",
        "en-CN;q=1, zh-Hans-CN;q=0.9").WithHeader("MT-Lat", user.Lat).WithHeader("MT-K",
        "1675213490331").WithHeader("MT-Lng", user.Lng).WithHeader("Host",
        "app.moutai519.com.cn").WithHeader("MT-User-Tag", "0").WithHeader("Accept", "*/*")
        .WithHeader("MT-Network-Type", "WIFI").WithHeader("MT-Token", user.Token)
        .WithHeader("MT-Team-ID", "").WithHeader("MT-Info", "028e7f96f6369cafe1d105579c5b9377")
        .WithHeader("MT-Device-ID", "2F2075D0-B66C-4287-A903-DBFF6358342A")
        .WithHeader("MT-Request-ID", "167560018873318465").WithHeader("MT-APP-Version", await
        GetMtVersion()).WithHeader("User-Agent", "iOS;16.3;Apple;?unrecognized?")
        .AllowAnyHttpStatus().PostStringAsync(requestBody);
        if (response.StatusCode is 200 or 480)
        {
            var responseString = await response.GetStringAsync();
            var responseJson = JObject.Parse(responseString);
            var logEntity = new LogEntity()
            {
                CreateTime = DateTime.Now,
                MobilePhone = user.Mobile,
                Content = $"[userId]:{user.UserId} [shopId]:{itemId}",
                Response = responseString,
                Status = responseJson["code"].Value<int>() == 2000 ? "预约成功" : "预约失败"
            };
            return logEntity;
        }
        else
        {
            var logEntity = new LogEntity()
            {
                CreateTime = DateTime.Now,
                MobilePhone = user.Mobile,
                Content = $"[userId]:{user.UserId} [shopId]:{itemId}",
                Response = $"本次抢购会话已过期,请手动刷新一下商品列表和店铺列表后重试",
                Status = "预约失败"
            };
            return logEntity;
        }
    }

    /// <summary>
    /// 获取mtSessionId
    /// </summary>
    /// <returns></returns>
    private async Task<string> GetCurrentSessionIdAsync()
    {
        string mtSessionId = "";
        _memoryCache.TryGetValue(_cacheSessionId, out mtSessionId);
        if (!string.IsNullOrEmpty(mtSessionId)) return mtSessionId;
        else
            return await RefreshSessionIdAsync();
    }

    /// <summary>
    /// 刷新mtSessionId
    /// </summary>
    /// <returns></returns>
    public async Task<string> RefreshSessionIdAsync()
    {
        var milliseconds = GetNowMilliseconds();
        var responseStr = await $"{_productUrl}{milliseconds}".GetStringAsync();
        var jObject = JObject.Parse(responseStr);
        if (jObject.GetValue("code").Value<int>() == 2000)
        {
            var dataJObject = jObject["data"];
            var sessionId = dataJObject["sessionId"].Value<string>();
            _memoryCache.Set(_cacheSessionId, sessionId);
            return sessionId;
        }
        return "";
    }

    /// <summary>
    /// AES CBC加密函数
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    private static string EncryptAES_CBC(string input)
    {
        var key = Encoding.UTF8.GetBytes(AesKey);
        var iv = Encoding.UTF8.GetBytes(AesIv);

        using var aes = Aes.Create();
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        aes.Key = key;
        aes.IV = iv;

        var encryptor = aes.CreateEncryptor();

        var inputBytes = Encoding.UTF8.GetBytes(input);
        var encryptedBytes = encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);

        var base64String = Convert.ToBase64String(encryptedBytes);
        return base64String;
    }

    /// <summary>
    /// 获得i茅台版本号
    /// </summary>
    /// <returns></returns>
    private async Task<string> GetMtVersion()
    {
        string mtVersion = "";
        _memoryCache.TryGetValue(_cacheVersion, out mtVersion);
        if (!string.IsNullOrEmpty(mtVersion)) return mtVersion;
        var htmlSource = await _versionUrl.GetStringAsync();
        var pattern = new Regex(@"new__latest__version\"">(.*?)<\/p>", RegexOptions.Singleline);
        var matcher = pattern.Match(htmlSource);
        var replace = "";
        if (!matcher.Success) return replace;
        mtVersion = matcher.Groups[1].Value;
        replace = mtVersion.Replace("版本 ", "");
        _memoryCache.Set(_cacheVersion, replace);
        return replace;
    }

    #endregion 预约

    #region 发送验证码

    /// <summary>
    /// 向手机号发送验证码
    /// </summary>
    /// <param name="phone"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<bool> SendCode(string phone)
    {
        var client = new HttpClient();
        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        var values = new Dictionary<string, string>
        {
            { "mobile", phone },
            { "md5", Signature(phone,timestamp) },
            {"timestamp",timestamp+""}
        };
        var content = new StringContent(JsonConvert.SerializeObject(values), Encoding.UTF8, "application/json");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
        client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "iOS;16.3;Apple;?unrecognized?");
        client.DefaultRequestHeaders.Add("MT-Lat", _MTLat);
        client.DefaultRequestHeaders.Add("MT-K", "1675213490331");
        client.DefaultRequestHeaders.Add("MT-Lng", _MTLng);
        client.DefaultRequestHeaders.Add("Host", "app.moutai519.com.cn");
        client.DefaultRequestHeaders.Add("MT-User-Tag", "0");
        client.DefaultRequestHeaders.Add("MT-Network-Type", "WIFI");
        client.DefaultRequestHeaders.TryAddWithoutValidation("MT-Team-ID", "");
        client.DefaultRequestHeaders.Add("MT-Info", "028e7f96f6369cafe1d105579c5b9377");
        client.DefaultRequestHeaders.Add("MT-Device-ID", "2F2075D0-B66C-4287-A903-DBFF6358342A");
        client.DefaultRequestHeaders.Add("MT-Bundle-ID", "com.moutai.mall");
        client.DefaultRequestHeaders.Add("Accept-Language", "en-CN;q=1, zh-Hans-CN;q=0.9");
        client.DefaultRequestHeaders.Add("MT-Request-ID", "167560018873318465");
        client.DefaultRequestHeaders.Add("MT-APP-Version", await GetMtVersion());
        client.DefaultRequestHeaders.Add("MT-R", "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Length", "93");
        client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br");
        client.DefaultRequestHeaders.Add("Connection", "keep-alive");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
        var response = await client
            .PostAsync(_codeUrl, content);
        var responseString = await response.Content.ReadAsStringAsync();
        var responseJson = JObject.Parse(responseString);
        var code = (string)responseJson["code"];
        if (code == "2000") return true;
        throw new Exception(responseString);
    }

    private static string Signature(string content, long timestamp)
    {
        var text = _salt + content + timestamp;
        using var md = MD5.Create();
        var hashBytes = md.ComputeHash(Encoding.UTF8.GetBytes(text));
        var sb = new StringBuilder();
        foreach (var b in hashBytes)
        {
            sb.Append($"{b:X2}");
        }
        return sb.ToString().ToLower();
    }

    #endregion 发送验证码

    #region 登录

    public async Task<UserEntity> Login(string phone, string code)
    {
        var client = new HttpClient();
        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        var values = new Dictionary<string, string>
     {
         { "mobile", phone },
         { "ydToken", "" },
         {"vCode",code},
         {"ydLogId",""},
         {"md5",Signature(phone + code + "" + "",timestamp)},
         {"timestamp",timestamp+""},
         {"MT-APP-Version",await GetMtVersion()}
     };
        var content = new StringContent(JsonConvert.SerializeObject(values), Encoding.UTF8, "application/json");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
        client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "iOS;16.3;Apple;?unrecognized?");
        client.DefaultRequestHeaders.Add("MT-Lat", _MTLat);
        client.DefaultRequestHeaders.Add("MT-K", "1675213490331");
        client.DefaultRequestHeaders.Add("MT-Lng", _MTLng);
        client.DefaultRequestHeaders.Add("Host", "app.moutai519.com.cn");
        client.DefaultRequestHeaders.Add("MT-User-Tag", "0");
        client.DefaultRequestHeaders.Add("MT-Network-Type", "WIFI");
        client.DefaultRequestHeaders.TryAddWithoutValidation("MT-Team-ID", "");
        client.DefaultRequestHeaders.Add("MT-Info", "028e7f96f6369cafe1d105579c5b9377");
        client.DefaultRequestHeaders.Add("MT-Device-ID", "2F2075D0-B66C-4287-A903-DBFF6358342A");
        client.DefaultRequestHeaders.Add("MT-Bundle-ID", "com.moutai.mall");
        client.DefaultRequestHeaders.Add("Accept-Language", "en-CN;q=1, zh-Hans-CN;q=0.9");
        client.DefaultRequestHeaders.Add("MT-Request-ID", "167560018873318465");
        client.DefaultRequestHeaders.Add("MT-APP-Version", await GetMtVersion());
        client.DefaultRequestHeaders.Add("MT-R", "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Length", "93");
        client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br");
        client.DefaultRequestHeaders.Add("Connection", "keep-alive");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
        var response = await client
            .PostAsync(_loginUrl, content);
        var responseString = await response.Content.ReadAsStringAsync();
        var responseJson = JObject.Parse(responseString);
        var responseCode = (string)responseJson["code"];
        if (responseCode != "2000")
        {
            var error = responseJson.TryGetValue("message", out var value) ? value.Value<string>() : responseString;
            throw Oops.Oh(error);
        }
        else
        {
            var model = responseJson["data"];
            UserEntity userEntity = new UserEntity()
            {
                UserId = (long)model["userId"],
                UserName = (string)model["userName"],
                Mobile = phone,
                IdCode = (string)model["idCode"],
                Token = (string)model["token"],
                Cookie = (string)model["cookie"],
                Did = (string)model["did"],
                IsDelete = 0,
                CreateTime = DateTime.Now,
                ExpireTime = DateTime.Now.AddDays(30),
            };

            return userEntity;
        }
    }

    #endregion 登录
}